﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using SteroClood.MediaPlayers;
using SteroMood;
using SteroMood.StereoMood_Data;
using Microsoft.WindowsAPICodePack.Taskbar;

namespace SteroClood
{
    public partial class StereoCloodForm : Form
    {
        /// <summary>
        /// Store window size when we minimize it (mini player) so that we can restore it properly
        /// </summary>
        private Point _previousSize = new Point();

        private readonly GlobalKeyboardHook _globalHook;
        private bool _windowsKeyDown = false;

        /// <summary>
        /// Song currently played & displayed
        /// </summary>
        private Song _playingSong = null;

        private IMediaPlayer _mediaPlayer;

        /// <summary>
        /// Songs currently displayed
        /// </summary>
        private Songs _songsDisplayed = null;

        /// <summary>
        /// Lock used to protect concurrent action to takes place
        /// </summary>
        private readonly object _actionLock = new object();

        private ListViewColumnSorter lvwColumnSorter;
        

        private readonly List<Song> _historySong = new List<Song>();

        private Microsoft.WindowsAPICodePack.Taskbar.ThumbnailToolBarButton _thumbPrev;
        private Microsoft.WindowsAPICodePack.Taskbar.ThumbnailToolBarButton _thumbNext;
        private Microsoft.WindowsAPICodePack.Taskbar.ThumbnailToolBarButton _thumbPlayPause;
        private TabbedThumbnail _taskBarPreview;
        /// <summary>
        /// Return the Assembly directory path
        /// </summary>
        static public string AssemblyDirectory
        {
            get
            {
                string codeBase = Assembly.GetExecutingAssembly().CodeBase;
                UriBuilder uri = new UriBuilder(codeBase);
                string path = Uri.UnescapeDataString(uri.Path);
                return Path.GetDirectoryName(path);
            }
        }

        public StereoCloodForm()
        {
            InitializeComponent();
            lvwColumnSorter = new ListViewColumnSorter();
            lvSongs.ListViewItemSorter = lvwColumnSorter;
            _globalHook = new GlobalKeyboardHook();

            _mediaPlayer = new WmpMediaPlayer();
            if (_mediaPlayer.Initialize() == false)
            {
                _mediaPlayer = new FmodMediaPlayer();
                _mediaPlayer.Initialize();
            }
        }
  
        private void EnqueueAction(StereoCloodAction actionToDo)
        {
            ThreadPool.QueueUserWorkItem(PerformActionThreadPoolCallback, actionToDo);
        }

        private void PerformActionThreadPoolCallback(object actionObj)
        {
            StereoCloodAction actionToDo = actionObj as StereoCloodAction;
            if (actionToDo == null)
                return;

            lock (_actionLock)
            {
                switch (actionToDo.Action)
                {
                    case StereoCloodActionType.GetSongsForTag:
                        {
                            Tag selectedTag = actionToDo.Value as Tag;
                            if (selectedTag == null)
                            {
                                return;
                            }

                            SetStatus("Retrieving songs for tag: "+selectedTag.Value, 0);
                            Search search = StereoMoodClient.SearchSongs(selectedTag.MoodOrActivity, selectedTag.Value);
                            if (search != null)
                            {
                                _songsDisplayed = search.SongsContainer;
                                UpdateSongs(search.SongsContainer);
                            }
                            PlayNext();
                        }
                        break;
                    case StereoCloodActionType.PlaySong:
                        {
                            Song song = actionToDo.Value as Song;
                            if (song == _playingSong)
                            {
                                return;
                            }
                            
                            if(song == null)
                            {
                                song = FindNextSong();
                            }

                            do
                            {
                                if (_mediaPlayer.LoadSong(song.AudioUrl))
                                {
                                    break;
                                }
                                song = FindNextSong();
                            } while (true);

                            _playingSong = song;
                            _historySong.Add(song);
                            if (_historySong.Count > 10)
                            {
                                //Remove Last
                                _historySong.RemoveAt(0);
                            }
                            UpdateSongDetail(song);
                        }
                        break;
                    case StereoCloodActionType.GetTopTags:
                        {
                            SetStatus("Retrieving top tags", 0);
                            Tags topTags = StereoMoodClient.GetTopTags();
                            if (topTags != null)
                            {
                                UpdateTags(topTags);
                            }
                            
                        }
                        break;
                }
                SetStatus(null, -1);
            }

        }        

        private Song FindNextSong()
        {          
            Random rndNumbers = new Random(Environment.TickCount);
            Song song = _songsDisplayed.SongsContainer[rndNumbers.Next(_songsDisplayed.SongsContainer.Count)];
            return song;
        }

        #region UI Updaters

        private void UpdateTags(Tags tags)
        {
            if (lvTags.InvokeRequired)
            {
                lvTags.BeginInvoke(new Action<Tags>(UpdateTags), tags);
                return;
            }

            // Update Tags list view
            lvTags.Items.Clear();
            lvTags.BeginUpdate();
            foreach (Tag topTag in tags.TagsList)
            {
                ListViewItem lvItem = lvTags.Items.Add(topTag.ToString());
                lvItem.Tag = topTag;
            }
            lvTags.EndUpdate();
            lvTags.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        }
        private void UpdateSongs(Songs songs)
        {
            if (lvSongs.InvokeRequired)
            {
                lvSongs.BeginInvoke(new Action<Songs>(UpdateSongs), songs);
                return;
            }

            lvSongs.Items.Clear();
            lvSongs.BeginUpdate();
            foreach (Song song in songs.SongsContainer)
            {
                ListViewItem lvItem = lvSongs.Items.Add(song.Artist);
                lvItem.SubItems.Add(song.Album);
                lvItem.SubItems.Add(song.Title);
                lvItem.Tag = song;
                lvItem.ToolTipText = song.ToString();
            }
            lvSongs.EndUpdate();
            lvSongs.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        }
        private void UpdateSongDetail(Song song)
        {
            if (albumArt.InvokeRequired)
            {
                albumArt.BeginInvoke(new Action<Song>(UpdateSongDetail), song);
                return;
            }
            if (song != null)
            {
                albumArt.ImageLocation = song.ImageUrl;
                labelArtist.Text = song.Artist;
                labelAlbum.Text = song.Album;
                labelTitle.Text = song.Title;

                foreach (ListViewItem item in lvSongs.Items)
                {
                    if (item.Tag is Song && item.Tag as Song == song)
                    {
                        item.Selected = true;
                        item.Focused = true;
                        lvSongs.TopItem = item;
                        break;
                    }
                }

                this.Text = song.ToString();

                UpdateTaskbarPreview(song);
            }
            else
            {
                labelStatus.Text = "Song not found";
                this.Text = "Stereoclood - http://stereoclood.codeplex.com";
            }
        }
        #endregion

        #region UI Callbacks
        private void StereoCloodFormLoad(object sender, EventArgs e)
        {
            try
            {
                _globalHook.KeyDown += new KeyEventHandler(OnGlobalKeyDown);
                _globalHook.KeyUp += new KeyEventHandler(OnGlobalKeyUp);

                _mediaPlayer.OnEndMusicEvent = new EndTackEventHandler(EndTrackHandler);

                InitializeWindowsTaskbar();
            }
            catch (Exception ex)
            {
                string str = ex.Message;
            }
        }

        #region Windows 7 Taskbar

        private void InitializeWindowsTaskbar()
        {
            if (TaskbarManager.IsPlatformSupported)
            {
                // Add a new preview 
                _taskBarPreview = new TabbedThumbnail(this.Handle, albumArt.Handle);

                TaskbarManager.Instance.TabbedThumbnail.AddThumbnailPreview(_taskBarPreview);

                // Create our Thumbnail toolbar buttons for the Browser doc
                _thumbPrev = new ThumbnailToolBarButton(Properties.Resources.prev_ico, "Previous");
                _thumbPrev.Click += new EventHandler<ThumbnailButtonClickedEventArgs>(_thumbPrev_Click);

                _thumbNext = new ThumbnailToolBarButton(Properties.Resources.next_ico, "Next");
                _thumbNext.Click += new EventHandler<ThumbnailButtonClickedEventArgs>(_thumbNext_Click);

                _thumbPlayPause = new ThumbnailToolBarButton(Properties.Resources.play_ico, "Play/Pause");
                _thumbPlayPause.Click += new EventHandler<ThumbnailButtonClickedEventArgs>(_thumbPlayPause_Click);

                // Add thumbnail toolbar buttons
                TaskbarManager.Instance.ThumbnailToolBars.AddButtons(albumArt.Handle, _thumbPrev, _thumbPlayPause, _thumbNext);

                _taskBarPreview.SetWindowIcon(this.Icon.Handle);
                _taskBarPreview.SetImage(Properties.Resources.default_album_large);
            }
        }

        /// <summary>
        /// Helper method to update the thumbnail preview for a given tab page.
        /// </summary>
        private void UpdateTaskbarPreview(Song song)
        {
            if (TaskbarManager.IsPlatformSupported)
            {
                Bitmap bitmap = null;
                try
                {
                    if(!string.IsNullOrEmpty(song.ImageUrl))
                    {
                        System.Net.WebRequest request = System.Net.WebRequest.Create(song.ImageUrl);
                        System.Net.WebResponse response = request.GetResponse();
                        System.IO.Stream responseStream = response.GetResponseStream();
                        if(responseStream != null)
                        {
                            bitmap = new Bitmap(responseStream);    
                        }
                    }
                }
                catch (System.Net.WebException)
                {
                    bitmap = null;
                }
                if (bitmap == null)
                {
                    bitmap = global::SteroClood.Properties.Resources.default_album_large;
                }
                _taskBarPreview.Title = song.ToString();
                _taskBarPreview.SetWindowIcon(this.Icon.Handle);
                _taskBarPreview.Tooltip = song.ToString();
                _taskBarPreview.SetImage(bitmap);
            }
        }

        void _thumbPlayPause_Click(object sender, ThumbnailButtonClickedEventArgs e)
        {
            PlayPause();
        }

        void _thumbNext_Click(object sender, ThumbnailButtonClickedEventArgs e)
        {
            PlayNext();
        }

        void _thumbPrev_Click(object sender, ThumbnailButtonClickedEventArgs e)
        {
            PlayPrev();
        }
        #endregion

        public void OnGlobalKeyDown(object sender, KeyEventArgs keyArg)
        {
            if (keyArg.KeyCode == Keys.LWin || keyArg.KeyCode == Keys.RWin)
            {
                _windowsKeyDown = true;
            }

            //TODO: Let the keys & modifiers configurable
            if ((keyArg.Alt) && _windowsKeyDown)
            {
                switch (keyArg.KeyCode)
                {
                    case Keys.B:
                        PlayNext();
                        keyArg.Handled = true;
                        break;
                    case Keys.C:
                        PlayPause();
                        keyArg.Handled = true;
                        break;
                    case Keys.W:
                        PlayPrev();
                        keyArg.Handled = true;
                        break;
                }
            }

            // Handle the natural multimedia Keys without regards of any modifiers
            switch (keyArg.KeyCode)
            {
                case Keys.MediaNextTrack:
                    PlayNext();
                    keyArg.Handled = true;
                    break;
                case Keys.MediaStop:
                case Keys.MediaPlayPause:
                    keyArg.Handled = true;
                    PlayPause();
                    break;
                case Keys.MediaPreviousTrack:
                    keyArg.Handled = true;
                    PlayPrev();
                    break;
            }

        }

        public void OnGlobalKeyUp(object sender, KeyEventArgs keyArg)
        {
            if (keyArg.KeyCode == Keys.LWin || keyArg.KeyCode == Keys.RWin)
            {
                _windowsKeyDown = false;
            }
        }

        private void EndTrackHandler()
        {
            PlayNext();
        }

        private void NextPictureBoxClick(object sender, EventArgs e)
        {
            PlayNext();
        }

        private void PrevPictureBoxClick(object sender, EventArgs e)
        {
            PlayPrev();
        }

        private void VolumeTrackBarValueChanged(object sender, EventArgs e)
        {
            _mediaPlayer.SetVolume(volumeTrackBar.Value);
        }

        private void StatusTimerTick(object sender, EventArgs e)
        {
            if (_mediaPlayer != null)
            {
                MediaPlayerStatus status = _mediaPlayer.GetStatus();

                if (status != null)
                {
                    playPausePictureBox.Image = status.IsPaused ? global::SteroClood.Properties.Resources.play_32 : global::SteroClood.Properties.Resources.pause_32;
                    labelStatus.Text = status.Status;
                }
            }
        }

        private void PlayPausePictureBoxClick(object sender, EventArgs e)
        {
            PlayPause();
        }

        private void StereoCloodFormShown(object sender, EventArgs e)
        {
            if (StereoMoodClient.Connected == false)
            {
                Login login = new Login();
                if (login.ShowDialog(this) == DialogResult.OK)
                {
                    EnqueueAction(new StereoCloodAction() { Action = StereoCloodActionType.GetTopTags });
                }
                else
                {
                    MessageBox.Show("You are not connected to StereoMood, StereoClood will close.");
                    this.Close();
                }
            }
        }

        private void ExpandCollapseClick(object sender, EventArgs e)
        {
            if (this.FormBorderStyle == FormBorderStyle.Sizable)
            {
                this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
                this.MaximizeBox = false;
                _previousSize.X = this.Width;
                _previousSize.Y = this.Height;
                int borderAndTitleSize = this.Height - this.ClientSize.Height;
                this.Height = albumArt.Height + borderAndTitleSize + 2;
                this.Width = 492;
                displayStatus.Text = "Maxi Player";
                expandCollapse.Image = global::SteroClood.Properties.Resources.expand;
            }
            else
            {
                this.MaximizeBox = true;
                this.Width = _previousSize.X;
                this.Height = _previousSize.Y;
                this.FormBorderStyle = FormBorderStyle.Sizable;
                displayStatus.Text = "Mini Player";
                expandCollapse.Image = global::SteroClood.Properties.Resources.collapse;
            }
        }
        #endregion

        #region Playback control
        public void PlayPrev()
        {
            if (_historySong.Count > 1)
            {
                Song prevSong = _historySong[_historySong.Count - 2];
                _historySong.RemoveAt(_historySong.Count - 1);
                _historySong.RemoveAt(_historySong.Count - 1);
                EnqueueAction(new StereoCloodAction() { Action = StereoCloodActionType.PlaySong, Value = prevSong });
            }
        }

        public void PlayPause()
        {
            bool paused = _mediaPlayer.PlayPause();
            playPausePictureBox.Image = paused ? global::SteroClood.Properties.Resources.play_32 : global::SteroClood.Properties.Resources.pause_32;
        }

        public void PlayNext()
        {
            EnqueueAction(new StereoCloodAction() { Action = StereoCloodActionType.PlaySong });
        }
        #endregion

        private void ExportPlaylistToolStripMenuItemClick(object sender, EventArgs e)
        {
            if (lvTags.SelectedItems.Count > 0)
            {
                ListViewItem lvTag = lvTags.SelectedItems[0];
                Tag tag = lvTag.Tag as Tag;
                if (tag != null)
                {
                    List<Song> songs = (from ListViewItem item in lvSongs.Items select item.Tag).OfType<Song>().ToList();
                    backgroundWorkerPlaylist.RunWorkerAsync(new object[] { tag, songs });
                }
            }
        }

        private void SetStatus(string message, int progress)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Action<string, int>(SetStatus), message, progress);
                return;
            }

            bool bShow = false;

            if (progress >= 0 && progress <= 100)
            {
                toolStripProgressBar1.Value = progress;
                bShow = true;
            }

            if (string.IsNullOrEmpty(message) == false)
            {
                bShow = true;
                toolStripStatusLabel2.Text = message;
            }

            StatusStrip.Visible = bShow;
        }

        private void BackgroundWorkerPlaylistDoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            object[] parameter = e.Argument as object[];
            if (parameter != null && parameter.Length == 2)
            {
                Tag tag = parameter[0] as Tag;
                List<Song> songs = parameter[1] as List<Song>;

                if (tag != null && songs != null && songs.Count > 0)
                {
                    SetStatus("Exporting playlist for : " + tag.Value, -1);
                    TextWriter textWriter = new StreamWriter(Path.Combine(AssemblyDirectory, string.Format("{0}, {1} - {2}.m3u", (tag.MoodOrActivity == "mood" ? "Mood" : "Activity"), tag.Value, tag.Popularity)));
                    int count = 0;
                    foreach (Song song in songs)
                    {
                        count++;

                        SetStatus("Locating song : " + song.ToString(), count * 100 / lvSongs.Items.Count);
                        var request = (HttpWebRequest)WebRequest.Create(new Uri(song.AudioUrl));
                        request.Method = "HEAD";
                        request.AllowAutoRedirect = false;

                        using (var response = request.GetResponse() as HttpWebResponse)
                        {
                            if (response != null)
                            {
                                song.AudioUrl = response.GetResponseHeader("Location"); ;
                            }
                        }
                        

                        textWriter.WriteLine("#EXTM3U");
                        textWriter.WriteLine("#EXTINF:-1," + song.Album + " - " + song.Title);
                        textWriter.WriteLine(song.AudioUrl);
                    }
                    textWriter.Close();
                    SetStatus("Playlist export finished", 100);
                    SetStatus(null, -1);
                }
            }
        }

        private void BackgroundWorkerPlaylistProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
        {

        }

        private void BackgroundWorkerPlaylistRunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {

        }

        private void LvSongsDoubleClick(object sender, EventArgs e)
        {
            if (lvSongs.SelectedItems.Count > 0)
            {
                Song song = (Song)lvSongs.SelectedItems[0].Tag;
                statusTimer.Start();
                EnqueueAction(new StereoCloodAction
                {
                    Action = StereoCloodActionType.PlaySong,
                    Value = song
                });
            }
        }

        private void lvSongs_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            // Determine if clicked column is already the column that is being sorted.
            if (e.Column == lvwColumnSorter.SortColumn)
            {
                // Reverse the current sort direction for this column.
                if (lvwColumnSorter.Order == SortOrder.Ascending)
                {
                    lvwColumnSorter.Order = SortOrder.Descending;
                }
                else
                {
                    lvwColumnSorter.Order = SortOrder.Ascending;
                }
            }
            else
            {
                // Set the column number that is to be sorted; default to ascending.
                lvwColumnSorter.SortColumn = e.Column;
                lvwColumnSorter.Order = SortOrder.Ascending;
            }

            // Perform the sort with these new sort options.
            this.lvSongs.Sort();
        }

        private void LvTagsDoubleClick(object sender, EventArgs e)
        {
            if (StereoMoodClient.Connected)
            {
                if (lvTags.SelectedItems.Count > 0)
                {
                    Tag selectedTag = (Tag)lvTags.SelectedItems[0].Tag;
                    EnqueueAction(new StereoCloodAction() { Action = StereoCloodActionType.GetSongsForTag, Value = selectedTag });
                }
            }
            else
            {
                MessageBox.Show("Stereoclood is not connected");
            }
        }
    }
}
